import { useCallback, useState,useReducer } from "react"
import { useMountedRef } from ".";
/**
 * 这个模块 用来管理项目中的异步操作的状态
 */
//定义状态接口
interface State<D>{
    error: Error | null,
    data: D | null,
    stat: 'idle' | 'loading' | 'error' | 'success'
}

//初始状态
const defaultInitialState: State<null> = {
    error:null,
    data:null,
    stat:'idle'
}

const defaultConfig = {
    throwOnError:false
}

const useSafeDiapatch = <T>(dispatch:(...args:T[])=>void)=>{
    const mountedRef = useMountedRef();
    return useCallback((...args:T[])=>(mountedRef.current ? dispatch(...args) : void 0),[dispatch,mountedRef]);
}

//自定义hook，用来设置异步状态 用useReducer改造
export const useAsync = <D>(initialState?:State<D>,initialConfig?:typeof defaultConfig)=>{
    const [state,dispatch] = useReducer((state:State<D>,action:Partial<State<D>>)=>({...state,...action}),{
        ...defaultInitialState,
        ...initialState
    });

    const config = {...defaultConfig,...initialConfig};
    
    const [retry,setRetry] = useState(()=>()=>{});

    const safeDispatch = useSafeDiapatch(dispatch);

    const setData = useCallback((data:D)=>safeDispatch({
        error:null,
        data,
        stat:'success'
    }),[safeDispatch]);

    const setError = useCallback((error:Error)=>safeDispatch({
        error,
        data:null,
        stat:'error'
    }),[safeDispatch]);

    //用来触发异步请求
    const run = useCallback((promise:Promise<D>,runConfig?:{retry:()=>Promise<D>})=>{
        //先判断是否是Promise
        if(!promise || !promise.then){
            throw new Error('请传入Promise类型数据')
        }
        //触发则设状态为loading
        //在useCallback里设置setState，依赖项里又有state，会导致无限循环，
        //正确做法是不要依赖State，在setState里使用函数式写法
        safeDispatch({stat:'loading'});
        setRetry(()=>()=>{
            if(runConfig?.retry){
                run(runConfig.retry(),runConfig);
            }
        });
        return promise
        .then(data=>{
            setData(data);
            return data;
        })
        .catch(error=>{       
            setError(error);
            //内部的catch捕获了异常，外部就无法捕获，需要内部主动抛出
            if(config.throwOnError){
                return Promise.reject(error);
            }else{
                return error;
            }
        })
    },[config.throwOnError,setData,setError,safeDispatch]);

    return {
        isIdle:state.stat === 'idle',
        isLoading:state.stat === 'loading',
        isError:state.stat === 'error',
        isSuccess:state.stat === 'success',
        run,
        setData,
        setError,
        retry,
        ...state
    }
}